﻿#region NeoPatterns
// 
// NeoPatterns framework is released under LGPLv3 license.
// See http://www.opensource.org/licenses/lgpl-3.0.html for more details.
// 
// Please visit http://neopatterns.wikidot.com for more information about NeoPatterns
// 
// Copyright (c) Pascal Craponne 2008
// 
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using NeoPatterns.Patterns.Prototype;
using NeoPatterns.Patterns.UniformMutator;
using Reflector.CodeModel;
using Reflector.CodeModel.Memory;

namespace NeoPatterns.Reflector
{
    public static class ReflectorMutators
    {
        private static readonly IDictionary<Type, Type> mutableExpressionVersions = new Dictionary<Type, Type>();
        private static readonly IDictionary<Type, Type> mutableStatementVersions = new Dictionary<Type, Type>();

        static ReflectorMutators()
        {
            mutableExpressionVersions[typeof(IAddressDereferenceExpression)] = typeof(AddressDereferenceExpression);
            mutableExpressionVersions[typeof(IAddressOfExpression)] = typeof(AddressOfExpression);
            mutableExpressionVersions[typeof(IAddressOutExpression)] = typeof(AddressOutExpression);
            mutableExpressionVersions[typeof(IAddressReferenceExpression)] = typeof(AddressReferenceExpression);
            mutableExpressionVersions[typeof(IAnonymousMethodExpression)] = typeof(AnonymousMethodExpression);
            mutableExpressionVersions[typeof(IArgumentListExpression)] = typeof(ArgumentListExpression);
            mutableExpressionVersions[typeof(IArgumentReferenceExpression)] = typeof(ArgumentReferenceExpression);
            mutableExpressionVersions[typeof(IArrayCreateExpression)] = typeof(ArrayCreateExpression);
            mutableExpressionVersions[typeof(IArrayIndexerExpression)] = typeof(ArrayIndexerExpression);
            mutableExpressionVersions[typeof(IAssignExpression)] = typeof(AssignExpression);
            mutableExpressionVersions[typeof(IBaseReferenceExpression)] = typeof(BaseReferenceExpression);
            mutableExpressionVersions[typeof(IBinaryExpression)] = typeof(BinaryExpression);
            mutableExpressionVersions[typeof(IBlockExpression)] = typeof(BlockExpression);
            mutableExpressionVersions[typeof(ICanCastExpression)] = typeof(CanCastExpression);
            mutableExpressionVersions[typeof(ICastExpression)] = typeof(CastExpression);
            mutableExpressionVersions[typeof(IConditionExpression)] = typeof(ConditionExpression);
            mutableExpressionVersions[typeof(IDelegateCreateExpression)] = typeof(DelegateCreateExpression);
            mutableExpressionVersions[typeof(IDelegateInvokeExpression)] = typeof(DelegateInvokeExpression);
            mutableExpressionVersions[typeof(IEventReferenceExpression)] = typeof(EventReferenceExpression);
            mutableExpressionVersions[typeof(IFieldOfExpression)] = typeof(FieldOfExpression);
            mutableExpressionVersions[typeof(IFieldReferenceExpression)] = typeof(FieldReferenceExpression);
            mutableExpressionVersions[typeof(IGenericDefaultExpression)] = typeof(GenericDefaultExpression);
            mutableExpressionVersions[typeof(ILambdaExpression)] = typeof(LambdaExpression);
            mutableExpressionVersions[typeof(ILiteralExpression)] = typeof(LiteralExpression);
            mutableExpressionVersions[typeof(IMemberInitializerExpression)] = typeof(MemberInitializerExpression);
            mutableExpressionVersions[typeof(IMethodInvokeExpression)] = typeof(MethodInvokeExpression);
            mutableExpressionVersions[typeof(IMethodOfExpression)] = typeof(MethodOfExpression);
            mutableExpressionVersions[typeof(IMethodReferenceExpression)] = typeof(MethodReferenceExpression);
            mutableExpressionVersions[typeof(INullCoalescingExpression)] = typeof(NullCoalescingExpression);
            mutableExpressionVersions[typeof(IObjectCreateExpression)] = typeof(ObjectCreateExpression);
            mutableExpressionVersions[typeof(IPropertyIndexerExpression)] = typeof(PropertyIndexerExpression);
            mutableExpressionVersions[typeof(IPropertyReferenceExpression)] = typeof(PropertyReferenceExpression);
            mutableExpressionVersions[typeof(IQueryExpression)] = typeof(QueryExpression);
            mutableExpressionVersions[typeof(ISizeOfExpression)] = typeof(SizeOfExpression);
            mutableExpressionVersions[typeof(ISnippetExpression)] = typeof(SnippetExpression);
            mutableExpressionVersions[typeof(IStackAllocateExpression)] = typeof(StackAllocateExpression);
            mutableExpressionVersions[typeof(IThisReferenceExpression)] = typeof(ThisReferenceExpression);
            mutableExpressionVersions[typeof(ITryCastExpression)] = typeof(TryCastExpression);
            mutableExpressionVersions[typeof(ITypedReferenceCreateExpression)] = typeof(TypedReferenceCreateExpression);
            mutableExpressionVersions[typeof(ITypeOfExpression)] = typeof(TypeOfExpression);
            mutableExpressionVersions[typeof(ITypeOfTypedReferenceExpression)] = typeof(TypeOfTypedReferenceExpression);
            mutableExpressionVersions[typeof(ITypeReferenceExpression)] = typeof(TypeReferenceExpression);
            mutableExpressionVersions[typeof(IUnaryExpression)] = typeof(UnaryExpression);
            mutableExpressionVersions[typeof(IValueOfTypedReferenceExpression)] = typeof(ValueOfTypedReferenceExpression);
            mutableExpressionVersions[typeof(IVariableDeclarationExpression)] = typeof(VariableDeclarationExpression);
            mutableExpressionVersions[typeof(IVariableReferenceExpression)] = typeof(VariableReferenceExpression);

            mutableStatementVersions[typeof(IAttachEventStatement)] = typeof(AttachEventStatement);
            mutableStatementVersions[typeof(IBlockStatement)] = typeof(BlockStatement);
            mutableStatementVersions[typeof(IBreakStatement)] = typeof(BreakStatement);
            mutableStatementVersions[typeof(ICommentStatement)] = typeof(CommentStatement);
            mutableStatementVersions[typeof(IConditionStatement)] = typeof(ConditionStatement);
            mutableStatementVersions[typeof(IContinueStatement)] = typeof(ContinueStatement);
            mutableStatementVersions[typeof(IDebugBreakStatement)] = typeof(DebugBreakStatement);
            mutableStatementVersions[typeof(IDoStatement)] = typeof(DoStatement);
            mutableStatementVersions[typeof(IExpressionStatement)] = typeof(ExpressionStatement);
            mutableStatementVersions[typeof(IFixedStatement)] = typeof(FixedStatement);
            mutableStatementVersions[typeof(IForEachStatement)] = typeof(ForEachStatement);
            mutableStatementVersions[typeof(IForStatement)] = typeof(ForStatement);
            mutableStatementVersions[typeof(IGotoStatement)] = typeof(GotoStatement);
            mutableStatementVersions[typeof(ILabeledStatement)] = typeof(LabeledStatement);
            mutableStatementVersions[typeof(ILockStatement)] = typeof(LockStatement);
            mutableStatementVersions[typeof(IMemoryCopyStatement)] = typeof(MemoryCopyStatement);
            mutableStatementVersions[typeof(IMemoryInitializeStatement)] = typeof(MemoryInitializeStatement);
            mutableStatementVersions[typeof(IMethodReturnStatement)] = typeof(MethodReturnStatement);
            mutableStatementVersions[typeof(IRemoveEventStatement)] = typeof(RemoveEventStatement);
            mutableStatementVersions[typeof(ISwitchStatement)] = typeof(SwitchStatement);
            mutableStatementVersions[typeof(IThrowExceptionStatement)] = typeof(ThrowExceptionStatement);
            mutableStatementVersions[typeof(ITryCatchFinallyStatement)] = typeof(TryCatchFinallyStatement);
            mutableStatementVersions[typeof(IUsingStatement)] = typeof(UsingStatement);
            mutableStatementVersions[typeof(IWhileStatement)] = typeof(WhileStatement);

        }

        private static object CloneAsMutable(object immutableObject, IPrototyper prototyper, IDictionary<Type, Type> mutableVersions)
        {
            var implementedInterfaces = (from i in immutableObject.GetType().GetInterfaces()
                                         where mutableVersions.ContainsKey(i)
                                         select i).ToList();
            if (implementedInterfaces.Count > 1)
                throw new System.Exception("Something's wrong here");
            if (implementedInterfaces.Count == 0)
                throw new System.Exception("No handled interface");
            var implementedInterface = implementedInterfaces[0];
            return prototyper.CloneAs(immutableObject, implementedInterface, mutableVersions[implementedInterface]);
        }

        private static IExpression CloneExpressionAsMutable(IExpression expression, IPrototyper prototyper)
        {
            return (IExpression)CloneAsMutable(expression, prototyper, mutableExpressionVersions);
        }

        private static IStatement CloneStatementAsMutable(IStatement statement, IPrototyper prototyper)
        {
            return (IStatement)CloneAsMutable(statement, prototyper, mutableExpressionVersions);
        }

        public static void RegisterExpressionCloneAsMutable(this IUniformMutator uniformMutator, IPrototyper prototyper)
        {
            uniformMutator.RegisterCloneAsMutable<IExpression, IExpression>(e => CloneExpressionAsMutable(e, prototyper));
        }

        public static void RegisterStatementCloneAsMutable(this IUniformMutator uniformMutator, IPrototyper prototyper)
        {
            uniformMutator.RegisterCloneAsMutable<IStatement, IStatement>(s => CloneStatementAsMutable(s, prototyper));
        }
    }
}
